package com.gelvt.minissm.spring.core;

import com.gelvt.minissm.spring.lang.Nullable;

/**
 * 带嵌套异常的CheckedException
 *
 * @author: Elvin Zeng
 * @date: 17-7-11
 */
public abstract class NestedCheckedException extends Exception {

    /**
     * @param msg 详细消息
     */
    public NestedCheckedException(String msg) {
        super(msg);
    }

    /**
     * @param msg   详细消息
     * @param cause 内部异常
     */
    public NestedCheckedException(String msg, Throwable cause) {
        super(msg, cause);
    }


    /**
     * @return 详细信息。包含内部异常的信息
     */
    @Override
    @Nullable
    public String getMessage() {
        return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
    }


    /**
     * @return 最底层的异常。如果不存在则返回null。
     */
    @Nullable
    public Throwable getRootCause() {
        return NestedExceptionUtils.getRootCause(this);
    }

    /**
     * 解析出最底层的异常，或者它自己。与{@link #getRootCause()} 不同的是，
     * 如果没有最底层的异常，则会返回这个异常本身。
     *
     * @return 最底层的异常，或者它自己
     */
    public Throwable getMostSpecificCause() {
        Throwable rootCause = getRootCause();
        return (rootCause != null ? rootCause : this);
    }

    /**
     * 检查当前异常以及内部异常中是否包含指定类型的异常
     *
     * @param exType 要搜索的异常的类型
     * @return 是否存在某个嵌套是指定的类型
     */
    public boolean contains(@Nullable Class<?> exType) {
        if (exType == null) {
            return false;
        }
        if (exType.isInstance(this)) {
            return true;
        }
        Throwable cause = getCause();
        if (cause == this) {
            return false;
        }
        if (cause instanceof NestedCheckedException) {
            return ((NestedCheckedException) cause).contains(exType);
        } else {
            //  这么做是因为内部异常也可能不是spring中定义的异常
            while (cause != null) {
                if (exType.isInstance(cause)) {
                    return true;
                }
                if (cause.getCause() == cause) {
                    break;
                }
                cause = cause.getCause();
            }
            return false;
        }
    }
}
